/*
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections;

using Gtk;
using Mono.Unix;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Managers;
using DCSharp.Backend.Objects;

namespace DCSharp.Gui
{
	public class DownloadView : TransferView
	{
		private ConnectionManager connectionManager;

		private ActionGroup actionGroup;

		public DownloadView(ConnectionManager connectionManager) :
			this(connectionManager, null)
		{
		}

		public DownloadView(ConnectionManager connectionManager,
			TreeModel model) : base(model)
		{
			if (connectionManager == null)
			{
				throw new ArgumentNullException("connectionManager");
			}
			this.connectionManager = connectionManager;

			// User interface
			ActionEntry[] entries = new ActionEntry[] {
				new ActionEntry("Open", Stock.Open, Catalog.GetString("_Open"), null,
					Catalog.GetString("Open"),
					OnOpen),
				new ActionEntry("BrowseTo", null, Catalog.GetString("_Open Folder"), null,
					Catalog.GetString("Open folder containing file"),
					OnBrowseTo),
				new ActionEntry("FindSources", Stock.Find, Catalog.GetString("_Search For This File"), null,
					Catalog.GetString("Does a search for the selected file"),
					OnFindSources)
			};

			actionGroup = new ActionGroup("DownloadViewActions");
			actionGroup.Add(entries);

			actionGroup["Open"].Sensitive = false;
			actionGroup["BrowseTo"].Sensitive = false;
			actionGroup["FindSources"].Sensitive = false;

			// Columns
			TreeViewColumn column;
			CellRendererProgress progressRenderer;

			// Progress
			column = GetColumn("Progress");
			progressRenderer = (CellRendererProgress)column.CellRenderers[0];

			column.SetCellDataFunc(progressRenderer, SetProgressVisible);
		}

		#region Methods

		public FileDownloader[] GetSelectedDownloads(bool recurse)
		{
			return base.GetSelectedObjects<FileDownloader>(
				(int)DownloadStore.Column.Object, recurse);
		}

		protected override void OnPopulatePopup(Menu menu)
		{
			bool sensitive = false;
			bool browseSensitive = false;
			bool searchSensitive = false;

			TreeIter[] iters = GetSelectedIters();
			if (iters.Length == 1)
			{
				FileDownloader downloader = Model.GetValue(iters[0],
					(int)DownloadStore.Column.Object) as FileDownloader;

				if (downloader != null)
				{
					sensitive = browseSensitive = downloader.Progress == 1;
					searchSensitive = downloader.RemoteFile.TTH != null;
				}
				else
				{
					string path = Model.GetValue(iters[0],
						(int)DownloadStore.Column.Path) as string;

					sensitive = path != null;
				}
			}

			actionGroup["Open"].Sensitive = sensitive;
			actionGroup["BrowseTo"].Sensitive = browseSensitive;
			actionGroup["FindSources"].Sensitive = searchSensitive;

			// Populate the menu
			menu.Add(actionGroup["Open"].CreateMenuItem());
			menu.Add(actionGroup["BrowseTo"].CreateMenuItem());

			MenuItem item = new SeparatorMenuItem();
			menu.Add(item);
			item.Show();

			menu.Add(actionGroup["FindSources"].CreateMenuItem());

			base.OnPopulatePopup(menu);
		}

		protected override void OnRowActivated(TreePath path,
			TreeViewColumn column)
		{
			SourceInfo source;
			FileDownloader downloader;

			TreeIter iter;
			if (Model.GetIter(out iter, path))
			{
				// It's a download?
				downloader = Model.GetValue(iter,
					(int)DownloadStore.Column.Object) as FileDownloader;

				if (downloader != null && downloader.Progress == 1)
				{
					Util.OpenFile(System.IO.Path.Combine(downloader.SavePath,
						downloader.RemoteFile.Name));
					return;
				}
				// It's a source?
				if (GetSource(Model, iter, out source, out downloader) &&
					!source.Active && source.IsValid && source.User.IsOnline)
				{
					connectionManager.Reset(source.User);
					downloader.ForceStart(source);
					return;
				}
				else
				{
					// It's a directory?
					string p = Model.GetValue(iter, (int)DownloadStore.Column.Path) as string;
					if (p != null)
					{
						Util.OpenFile(p);
					}
				}
			}
			base.OnRowActivated(path, column);
		}

		#region CellDataFuncs

		protected override void SetPixbuf(TreeViewColumn column,
			CellRendererPixbuf pixbufRenderer, TreeModel model, TreeIter iter,
			object obj)
		{
			FileDownloader downloader = obj as FileDownloader;
			SourceInfo source = obj as SourceInfo;

			if (downloader != null)
			{
				pixbufRenderer.Pixbuf = Util.GetIconFromPath(downloader.RemoteFile.Name);
			}
			else if (source != null)
			{
				if (source.Active)
				{
					pixbufRenderer.Pixbuf = null;
					pixbufRenderer.StockId = Stock.GoDown;
				}
				else
				{
					pixbufRenderer.Pixbuf = source.User.IsOnline ?
						IconManager.UserOnline : IconManager.UserOffline;
				}
			}
			else
			{
				string icon = Stock.Directory;
				string path = model.GetValue(iter,
					(int)DownloadStore.Column.Path) as string;

				if (path != null)
				{
					if (path == System.IO.Path.GetPathRoot(path))
					{
						icon = Stock.Harddisk;
					}
					else if (path == Environment.GetFolderPath(Environment.SpecialFolder.Personal))
					{
						pixbufRenderer.Pixbuf = IconManager.GetIcon("user-home", 16);
						return;
					}
				}
				pixbufRenderer.Pixbuf = RenderIcon(icon, IconSize.Menu, null);
			}
		}

		protected override void SetStatusText(TreeViewColumn column,
			CellRendererText textRenderer, TreeModel model, TreeIter iter,
			object obj)
		{
			FileDownloader downloader = obj as FileDownloader;
			SourceInfo source = obj as SourceInfo;

			if (downloader != null)
			{
				textRenderer.Text = Util.GetStatus(downloader);

				SetTextStyle(column, textRenderer, model, iter, obj);
			}
			else if (source != null)
			{
				RequestStatus status = connectionManager.GetUserStatus(source.User);
				string text = null;

				if (source.Active)
				{
					text = String.Format("{0}/s",
						Util.FormatFileSize((long)source.DownloadSpeed));
				}
				else if (!source.IsValid)
				{
					text = GetErrorText(source.Error) ??
						Catalog.GetString("Invalid source");
				}
				else if (status == RequestStatus.Connecting)
				{
					int attempt;
					if (connectionManager.IsConnectingTo(source.User, out attempt))
					{
						text = String.Format(Catalog.GetString("Connecting... (attempt {0} of {1})"),
							attempt, ConnectionManager.MaxFailedAttempts);
					}
					else
					{
						text = Catalog.GetString("Connecting...");
					}
				}
				else if (status == RequestStatus.UserBusy)
				{
					text = Catalog.GetString("No slots available");
				}
				else if (status == RequestStatus.TooManyFailedAttempts)
				{
					text = Catalog.GetString("Connection attempts failed");
				}
				textRenderer.Text = text;

				SetTextStyle(column, textRenderer, model, iter, obj);
			}
			else
			{
				textRenderer.Text = null;
			}
		}

		protected override void SetTextStyle(TreeViewColumn column,
			CellRendererText textRenderer, TreeModel model, TreeIter iter,
			object obj)
		{
			FileDownloader downloader = obj as FileDownloader;
			SourceInfo source = obj as SourceInfo;

			if (downloader != null)
			{
				if (downloader.State == DownloadState.Done)
				{
					textRenderer.Foreground = "dark green";
				}
				else if (downloader.State == DownloadState.Loading)
				{
					textRenderer.Foreground = "dark blue";
				}
				else if (downloader.State == DownloadState.Stopped)
				{
					textRenderer.Foreground = downloader.Exception != null ?
						"dark red" : "dim grey";
				}
				else
				{
					textRenderer.Foreground = "black";
				}
			}
			else if (source != null)
			{
				textRenderer.Foreground = source.Active ? "black" :
					(source.IsValid ? "dim grey" : "dark red");
			}
			else
			{
				int progress = (int)model.GetValue(iter,
					(int)DownloadStore.Column.Progress);

				textRenderer.Foreground = progress == 100 ? "dark green" : "black";
			}
		}

		private static string GetErrorText(FileDownloadError error)
		{
			switch (error)
			{
				case FileDownloadError.FileNotAvailable:
					return Catalog.GetString("File not available");

				case FileDownloadError.ClientToOld:
					return Catalog.GetString("Client to old");

				case FileDownloadError.InvalidHashTree:
					return Catalog.GetString("Invalid data received");

				case FileDownloadError.ValidationError:
					return Catalog.GetString("Invalid data received");

				default:
					return null;
			}
		}

		private void SetProgressVisible(TreeViewColumn column,
			CellRenderer renderer, TreeModel model, TreeIter iter)
		{
			CellRendererProgress progressRenderer = renderer as CellRendererProgress;
			if (progressRenderer != null)
			{
				progressRenderer.Visible = !(model.GetValue(iter, (int)DownloadStore.Column.Object)
					is SourceInfo);
			}
		}

		#endregion

		private static bool GetSource(TreeModel model, TreeIter iter,
			out SourceInfo source, out FileDownloader downloader)
		{
			source = model.GetValue(iter, (int)DownloadStore.Column.Object)
				as SourceInfo;

			if (source != null)
			{
				TreeIter parent;
				if (model.IterParent(out parent, iter))
				{
					downloader = model.GetValue(parent,
						(int)DownloadStore.Column.Object) as FileDownloader;

					if (downloader != null)
					{
						return true;
					}
				}
			}
			source = null;
			downloader = null;
			return false;
		}

		private void OnOpen(object obj, EventArgs args)
		{
			TreeIter[] iters = GetSelectedIters();
			if (iters.Length == 1)
			{
				FileDownloader downloader = Model.GetValue(iters[0],
					(int)DownloadStore.Column.Object) as FileDownloader;

				if (downloader != null && downloader.Progress == 1)
				{
					Util.OpenFile(System.IO.Path.Combine(downloader.SavePath,
						downloader.RemoteFile.Name));
				}
				else
				{
					string path = Model.GetValue(iters[0],
						(int)DownloadStore.Column.Path) as string;
					if (path != null)
					{
						Util.OpenFile(path);
					}
				}
			}
		}

		private void OnBrowseTo(object obj, EventArgs args)
		{
			TreeIter[] iters = GetSelectedIters();
			if (iters.Length == 1)
			{
				FileDownloader downloader = Model.GetValue(iters[0],
					(int)DownloadStore.Column.Object) as FileDownloader;

				if (downloader != null && downloader.Progress == 1)
				{
					Util.OpenFile(downloader.SavePath);
				}
			}
		}

		private void OnFindSources(object obj, EventArgs args)
		{
			TreeIter[] iters = GetSelectedIters();
			if (iters.Length == 1)
			{
				FileDownloader downloader = Model.GetValue(iters[0],
					(int)DownloadStore.Column.Object) as FileDownloader;

				if (downloader != null)
				{
					Globals.Gui.Search(new SearchInfo(downloader.RemoteFile.TTH));
				}
			}
		}

		#endregion
	}
}
